/*
 * Projet Cryptolib CPS3 ASIP:
 *
 * Couche C pure des traitements spécifiques du programme d'exemple
 *
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pkcs11.h"
#include "spec_controleur.h"


/*********************/
/* FONCTIONS ANNEXES */
/*********************/

/*
 * Réinitialise les identifiants (slot/session) et informations (carte/session) du contexte de réalisation des appels spécifiques
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 */
void reinitialiseIdentifiantsEtInfosContexteTraitementsSpecifiques(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS)
{
	pContexteTS->identifiantSlot = -1;
	pContexteTS->identifiantSession = -1;
	pContexteTS->identifiantObjet = 0;
	memset(&pContexteTS->infosCarte,0,sizeof(CK_TOKEN_INFO));
	memset(&pContexteTS->infosSession,0,sizeof(CK_SESSION_INFO));

}

/* 
 * Scanne tous les slots et mémorise dans le contexte la première carte supportée rencontrée
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @return Code retour
 */
CK_RV recuperePremiereCarte(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS) {

	CK_RV codeRetour;
	CK_ULONG tailleListeTousLesSlots;
	CK_SLOT_ID_PTR pListeTousLesSlotsAvecCarteSupportee = NULL_PTR;
	CK_ULONG tailleListeTousLesSlotsAvecCarteSupportee;
	int nbTentativesSiBufferTropPetit = 4;
	int i = 0;

	/* Réinitialisation des identifiants (slot/session) et informations (carte/session) du contexte de réalisation des appels spécifiques */
	reinitialiseIdentifiantsEtInfosContexteTraitementsSpecifiques(pContexteTS);

	/* Si les fonctions de la librairie sont disponibles */
	if(pContexteTS->pFonctionsP11 != NULL) {

		/*********************/
		/* DETECTION LECTEUR */
		/*********************/

		/* Récupération de la taille de la liste des slots */
		codeRetour = pContexteTS->pFonctionsP11->C_GetSlotList(CK_FALSE, NULL_PTR, &tailleListeTousLesSlots);

		if(codeRetour == CKR_OK) {

			if(tailleListeTousLesSlots == 0)
				codeRetour = CKR_AUCUN_LECTEUR_PRESENT;

			/* Sinon récupération de la liste des slots avec carte */
			else {

				/*******************/
				/* DETECTION CARTE */
				/*******************/

				/* C'est le choix de l'implémentation, dans un soucis d'améliorer les performances */
				tailleListeTousLesSlotsAvecCarteSupportee = tailleListeTousLesSlots;
				pListeTousLesSlotsAvecCarteSupportee = (CK_SLOT_ID_PTR) malloc(tailleListeTousLesSlotsAvecCarteSupportee*sizeof(CK_SLOT_ID));

				/* Récupération de la liste des slots avec carte */
				codeRetour = pContexteTS->pFonctionsP11->C_GetSlotList(CK_TRUE, pListeTousLesSlotsAvecCarteSupportee, &tailleListeTousLesSlotsAvecCarteSupportee);

				/* On boucle tant que le buffer pour la liste des slots est trop petit
				   On s'accorde nbTentativesSiBufferTropPetit tentatives */
				while(codeRetour == CKR_BUFFER_TOO_SMALL && i < nbTentativesSiBufferTropPetit) {

					pListeTousLesSlotsAvecCarteSupportee = (CK_SLOT_ID_PTR) realloc(pListeTousLesSlotsAvecCarteSupportee, tailleListeTousLesSlotsAvecCarteSupportee*sizeof(CK_SLOT_ID));

					/* Récupération de la liste des slots avec carte */
					codeRetour = pContexteTS->pFonctionsP11->C_GetSlotList(CK_TRUE, pListeTousLesSlotsAvecCarteSupportee, &tailleListeTousLesSlotsAvecCarteSupportee);

					i++;
				}

				if(codeRetour == CKR_OK) {

					if(tailleListeTousLesSlotsAvecCarteSupportee == 0)
							codeRetour = CKR_AUCUNE_CARTE_SUPPORTEE;

					else {

						/* Etude du nombre de cartes */

						if(tailleListeTousLesSlotsAvecCarteSupportee > 1) {

							/* Carte sélectionnée = Première carte de la liste
							   C’est le choix de l’implémentation du programme d’exemple. 
							   Une application développée en réutilisant les exemples de code fournis
							   pourrait à la place faire choisir par l’utilisateur la carte à sélectionner */
							pContexteTS->identifiantSlot = pListeTousLesSlotsAvecCarteSupportee[0];

						} else {
							
							/* Carte sélectionnée = La seule carte détectée */
							pContexteTS->identifiantSlot = pListeTousLesSlotsAvecCarteSupportee[0];

						}
					}
				}

				/* Liberation de la mémoire attribuée à la liste des slots */
				free(pListeTousLesSlotsAvecCarteSupportee);

			}
		}
	}
	else
		codeRetour = CKR_LISTE_FONCTIONS_NON_INITIALISEE;

	return codeRetour;
}

/* 
 * Ouvre une session sur la carte mémorisée dans le contexte
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @return Code retour
 */
CK_RV ouvreSession(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS) {

	CK_FLAGS flagTypeSessionAOuvrir = CKF_SERIAL_SESSION;

	/* Réinitialisation du contexte de session */
	pContexteTS->identifiantSession = -1;
	if(&pContexteTS->infosSession != NULL)
		memset(&pContexteTS->infosSession,0,sizeof(CK_SESSION_INFO));
	
	/* Initialisation des flags pour la lecture seule */
	if(!pContexteTS->estSessionLectureEcriture)
		flagTypeSessionAOuvrir |= CKS_RO_PUBLIC_SESSION;

	/* Initialisation des flags pour la lecture/ecriture */
	else if(pContexteTS->estSessionLectureEcriture)
		flagTypeSessionAOuvrir |= CKF_RW_SESSION;

	/* Ouverture de la session */
	return (pContexteTS->pFonctionsP11->C_OpenSession)(pContexteTS->identifiantSlot, flagTypeSessionAOuvrir, pContexteTS->pNomApplication, NULL, &(pContexteTS->identifiantSession));

}

/* 
 * Ouvre une session sur la première carte supportée rencontrée
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @return Code retour
 */
CK_RV ouvreSessionSurPremiereCarte(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS) {

	CK_RV codeRetour;

	//####/
	 DEBUT:
	//####/

		/* Récupération de la premiere carte rencontrée */
		codeRetour = recuperePremiereCarte(pContexteTS);

		if(codeRetour == CKR_OK) {

			/* Ouverture de la session */
			codeRetour = ouvreSession(pContexteTS);

			if(codeRetour != CKR_OK) {
			
				switch(codeRetour) {

					/* Si l’erreur retournée est l’une des suivantes on réessaie */
					case CKR_DEVICE_ERROR:
					case CKR_DEVICE_REMOVED:
					case CKR_SLOT_ID_INVALID:
					case CKR_TOKEN_NOT_PRESENT:
					case CKR_TOKEN_NOT_RECOGNIZED:

						goto DEBUT;

				}
			}
		}

		return codeRetour;
}

/*
 * Détermine si une authentification est nécessaire
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @param codeRetour - Code retour en cas d'exception
 * @return Booléen indiquant si une authentification est nécessaire
 */
CK_BBOOL authentificationNecessaire(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS, CK_RV* codeRetour) {

	*codeRetour = authentification(pContexteTS);

	if(*codeRetour == CKR_AUCUN_LECTEUR_PRESENT) {

		if((*pContexteTS->pDemandeConnexionLecteur)() == ANNULATION) {

			*codeRetour = CKR_INTERRUPTION_UTILISATEUR;
			return CK_FALSE;

		}
	}

	else if(*codeRetour == CKR_AUCUNE_CARTE_SUPPORTEE) {


		if((*pContexteTS->pDemandeInsertionCarte)() == ANNULATION) {

			*codeRetour = CKR_INTERRUPTION_UTILISATEUR;
			return CK_FALSE;

		}
	}

	/* Si l'utilisateur est authentifié, authentification non nécessaire */
	else if(*codeRetour == CKR_OK) {
		*codeRetour = CKR_UTILISATEUR_AUTHENTIFIE;
		return CK_FALSE;
	}

	/* Transposition du code retour pour le sans contact */
	else if(*codeRetour == CKR_MODE_SANS_CONTACT_UTILISATEUR_NON_AUTHENTIFIE) {
		*codeRetour = CKR_TRAITEMENT_NON_SUPPORTE_EN_MODE_SANS_CONTACT;
		return CK_FALSE;
	}

	/* S'il s'agit d'une erreur PKCS#11, on interrompt */
	else if(*codeRetour != CKR_UTILISATEUR_NON_AUTHENTIFIE)
		return CK_FALSE;

	/* Si on arrive ici l'utilisateur est non authentifié, et une authentification est donc nécessaire */
	return CK_TRUE;

}

/*
 * Mémorise dans le contexte, sur la base des informations carte, le nombre d'essais restants avant blocage
 *
 * @param typeUtilisateur - CKU_USER ou CKU_SO
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @return Code retour
 */
CK_RV infoNbEssaisCode(unsigned long typeUtilisateur, CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS) {

	CK_RV codeRetour;

	//####/
	 DEBUT:
	//####/

		codeRetour = CKR_OK;

		/* Réinitialisation des flags */
		if(typeUtilisateur == CKU_USER)
			pContexteTS->flagMessageEssaisRestantsAvantBlocageCodePorteur = (pContexteTS->flagNbEssaisPossiblesAvantBlocageCodePorteur == CKF_N_ESSAIS_AVANT_BLOCAGE)?CKF_AUCUN_CODE_PORTEUR_ERRONE:CKF_TROIS_ESSAIS_RESTANTS;
		else if(typeUtilisateur == CKU_SO)
			pContexteTS->flagMessageEssaisRestantsAvantBlocageCodeDeblocage = CKF_AUCUN_CODE_DEBLOCAGE_ERRONE;

		/* Réinitialisation des infos cartes */
		memset(&pContexteTS->infosCarte,0,sizeof(CK_TOKEN_INFO));

		/*	Obtention des informations de la carte */
		codeRetour = pContexteTS->pFonctionsP11->C_GetTokenInfo(pContexteTS->identifiantSlot, &(pContexteTS->infosCarte));
			
		if(codeRetour == CKR_OK) {

			/* Transposition du flag */
			if(typeUtilisateur == CKU_USER) {

				if(pContexteTS->infosCarte.flags & CKF_USER_PIN_FINAL_TRY)
					pContexteTS->flagMessageEssaisRestantsAvantBlocageCodePorteur = (pContexteTS->flagNbEssaisPossiblesAvantBlocageCodePorteur == CKF_N_ESSAIS_AVANT_BLOCAGE)?CKF_DERNIERE_TENTATIVE_CODE_PORTEUR_AVANT_BLOCAGE:CKF_UN_ESSAI_RESTANT;
				else if(pContexteTS->infosCarte.flags & CKF_USER_PIN_COUNT_LOW)
					pContexteTS->flagMessageEssaisRestantsAvantBlocageCodePorteur = (pContexteTS->flagNbEssaisPossiblesAvantBlocageCodePorteur == CKF_N_ESSAIS_AVANT_BLOCAGE)?CKF_UN_OU_PLUSIEURS_CODES_PORTEUR_ERRONES:CKF_DEUX_ESSAIS_RESTANTS;
				else if(pContexteTS->infosCarte.flags & CKF_USER_PIN_LOCKED)
					pContexteTS->flagMessageEssaisRestantsAvantBlocageCodePorteur = CKF_CODE_PORTEUR_BLOQUE;
				else
					pContexteTS->flagMessageEssaisRestantsAvantBlocageCodePorteur = (pContexteTS->flagNbEssaisPossiblesAvantBlocageCodePorteur == CKF_N_ESSAIS_AVANT_BLOCAGE)?CKF_AUCUN_CODE_PORTEUR_ERRONE:CKF_TROIS_ESSAIS_RESTANTS;

			} else if(typeUtilisateur == CKU_SO) {

				if(pContexteTS->infosCarte.flags & CKF_SO_PIN_FINAL_TRY)
					pContexteTS->flagMessageEssaisRestantsAvantBlocageCodeDeblocage = CKF_DERNIERE_TENTATIVE_CODE_DEBLOCAGE_AVANT_BLOCAGE;
				else if(pContexteTS->infosCarte.flags & CKF_SO_PIN_COUNT_LOW)
					pContexteTS->flagMessageEssaisRestantsAvantBlocageCodeDeblocage = CKF_UN_OU_PLUSIEURS_CODES_DEBLOCAGE_ERRONES;
				else if(pContexteTS->infosCarte.flags & CKF_SO_PIN_LOCKED)
					pContexteTS->flagMessageEssaisRestantsAvantBlocageCodeDeblocage = CKF_CODE_DEBLOCAGE_BLOQUE;
				else
					pContexteTS->flagMessageEssaisRestantsAvantBlocageCodeDeblocage = CKF_AUCUN_CODE_DEBLOCAGE_ERRONE;

			}

		} else {

			switch(codeRetour) {

				/* Si l’erreur retournée est l’une des suivantes on réessaie
				   On refait le traitement d’ouverture de session puis on retourne à l'analyse du nombre d'essais restants */
				case CKR_DEVICE_ERROR:
				case CKR_DEVICE_REMOVED:
				case CKR_SLOT_ID_INVALID:
				case CKR_TOKEN_NOT_PRESENT:
				case CKR_TOKEN_NOT_RECOGNIZED:

					ouvreSession(pContexteTS);
					goto DEBUT;

			}
		}

		return codeRetour;

}

/* 
 * Détermine le statut d'authentification de l'utilisateur
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @return Code retour
 */
CK_RV authentification(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS) {

	CK_RV codeRetour;

	//####/
	 DEBUT:
	//####/

		/* Ouverture de session 
			Si un identifiant de slot a été positionné sur le contexte on ouvre la session sur ce slot
			Sinon on fait une ouverture de session sur la première carte trouvée */
		if(pContexteTS->identifiantSlot != -1)
			codeRetour = ouvreSession(pContexteTS);
		else
			codeRetour = ouvreSessionSurPremiereCarte(pContexteTS);

		if(codeRetour == CKR_OK) {

			/* Si l'on a pas préalablement récupéré les infos cartes on le fait pour savoir si l'authentification est nécessaire */
			if(strlen(pContexteTS->infosCarte.serialNumber) == 0) {
				
				/*	Obtention des informations de la carte */
				codeRetour = pContexteTS->pFonctionsP11->C_GetTokenInfo(pContexteTS->identifiantSlot, &(pContexteTS->infosCarte));
				if(codeRetour != CKR_OK)
					goto DEBUT;

			}

			if(pContexteTS->infosCarte.flags & CKF_LOGIN_REQUIRED) {			

				/***************************/
				/* OBTENTION INFOS SESSION */
				/***************************/
				memset(&pContexteTS->infosSession,0,sizeof(pContexteTS->infosSession));
				codeRetour = (pContexteTS->pFonctionsP11->C_GetSessionInfo)(pContexteTS->identifiantSession, &(pContexteTS->infosSession));

				if(codeRetour == CKR_OK) {

					/***************************/
					/* CARACTERISATION SESSION */
					/***************************/

					/* Si la session est non loguée, l'utilisateur n'est pas authentifié */
					if((!pContexteTS->estSessionLectureEcriture && (pContexteTS->infosSession.state == CKS_RO_PUBLIC_SESSION))
							|| (pContexteTS->estSessionLectureEcriture && (pContexteTS->infosSession.state == CKS_RW_PUBLIC_SESSION)))
						codeRetour = CKR_UTILISATEUR_NON_AUTHENTIFIE;

					/* Si la session est loguée, l'utilisateur est déjà authentifié */
					else if((!pContexteTS->estSessionLectureEcriture && (pContexteTS->infosSession.state == CKS_RO_USER_FUNCTIONS))
							|| (pContexteTS->estSessionLectureEcriture && (pContexteTS->infosSession.state == CKS_RW_USER_FUNCTIONS)))
						codeRetour = CKR_OK/* Utilisateur authentifié */;

				} else {

					switch(codeRetour) {

						/* Si l’erreur retournée est l’une des suivantes on réessaie */
						case CKR_DEVICE_ERROR:
						case CKR_DEVICE_REMOVED:
						case CKR_SESSION_CLOSED:
						case CKR_SESSION_HANDLE_INVALID:

							goto DEBUT;

					}
				}

			} else {
					codeRetour = CKR_MODE_SANS_CONTACT_UTILISATEUR_NON_AUTHENTIFIE;
			}
		}

		return codeRetour;
}


/*
 * Mémorise dans le contexte, sur la base des informations carte, la (non)conformité du code
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @param codePorteur - code porteur à analyser
 * @return Code retour
 */
CK_RV infoConformiteCode(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS, char * codePorteur) {

	CK_RV codeRetour;

	//####/
	 DEBUT:
	//####/

		codeRetour = CKR_OK;

		/* Réinitialisation des flags */
		pContexteTS->flagMessageConformiteCodePorteur = CKF_CODE_PORTEUR_CONFORME;

		/* Réinitialisation des infos cartes */
		memset(&pContexteTS->infosCarte,0,sizeof(CK_TOKEN_INFO));

		if(codePorteur != NULL) {

			/*	Obtention des informations de la carte */
			codeRetour = pContexteTS->pFonctionsP11->C_GetTokenInfo(pContexteTS->identifiantSlot, &(pContexteTS->infosCarte));
			
			if(codeRetour == CKR_OK) {

				/* Analyse du code porteur
				   Si la longueur du code porteur n’est pas comprise entre les longueurs minimales et maximales autorisées par la carte
				   On retourne le code retour associé
				 */
				if(strlen(codePorteur) < pContexteTS->infosCarte.ulMinPinLen || strlen(codePorteur) > pContexteTS->infosCarte.ulMaxPinLen) {
					pContexteTS->flagMessageConformiteCodePorteur = CKF_LONGUEUR_CODE_PORTEUR_NON_CONFORME;
					codeRetour = CKR_PIN_LEN_RANGE;
				}

			} else {

				switch(codeRetour) {

					/* Si l’erreur retournée est l’une des suivantes on réessaie
					   on refait le traitement d’ouverture de session puis on retourne à l'analyse de la conformité du codePorteur */
					case CKR_DEVICE_ERROR:
					case CKR_DEVICE_REMOVED:
					case CKR_SLOT_ID_INVALID:
					case CKR_TOKEN_NOT_PRESENT:
					case CKR_TOKEN_NOT_RECOGNIZED:

						ouvreSession(pContexteTS);
						goto DEBUT;

				}
			}

		} else {

			/* Si le code porteur est nul, il est considérer comme invalide */
			pContexteTS->flagMessageConformiteCodePorteur = CKF_CODE_PORTEUR_NON_CONFORME;
			codeRetour = CKR_PIN_INVALID;

		}

		return codeRetour;

}

/* 
 * Scanne tous les slots avec carte pour créer un instantané mémorisant les associations (identifiantSlot/numeroSerieCarte)
 *
 * @param pFonctionsP11 - pointeur sur la liste des fonctions de la librairie PKCS#11
 * @param mapSlotsCartes - table de correspondanceSlotsCartes à remplir
 * @return Code retour
 */
CK_RV instantane(CK_FUNCTION_LIST_PTR pFonctionsP11, MAP_SLOTS_CARTES* mapSlotsCartes) {

	CK_RV codeRetour;
	CK_SLOT_ID_PTR pListeTousLesSlotsAvecCarteSupportee = NULL_PTR;
	CK_ULONG tailleListeTousLesSlotsAvecCarteSupportee = 16;
	int nbTentativesSiBufferTropPetit = 4;
	int i = 0;

	/* Si les fonctions de la librairie sont disponibles */
	if(pFonctionsP11 != NULL) {

		/*******************/
		/* DETECTION CARTE */
		/*******************/

		pListeTousLesSlotsAvecCarteSupportee = (CK_SLOT_ID_PTR) malloc(tailleListeTousLesSlotsAvecCarteSupportee*sizeof(CK_SLOT_ID));

		/* Récupération de la liste des slots avec carte */
		codeRetour = pFonctionsP11->C_GetSlotList(CK_TRUE, pListeTousLesSlotsAvecCarteSupportee, &tailleListeTousLesSlotsAvecCarteSupportee);

		/* On boucle tant que le buffer pour la liste des slots est trop petit
		   On s'accorde nbTentativesSiBufferTropPetit tentatives */
		while(codeRetour == CKR_BUFFER_TOO_SMALL && i < nbTentativesSiBufferTropPetit) {

			pListeTousLesSlotsAvecCarteSupportee = (CK_SLOT_ID_PTR) realloc(pListeTousLesSlotsAvecCarteSupportee, tailleListeTousLesSlotsAvecCarteSupportee*sizeof(CK_SLOT_ID));

			/* Récupération de la liste des slots avec carte */
			codeRetour = pFonctionsP11->C_GetSlotList(CK_TRUE, pListeTousLesSlotsAvecCarteSupportee, &tailleListeTousLesSlotsAvecCarteSupportee);

			i++;
		}

		if(codeRetour == CKR_OK) {

			if(tailleListeTousLesSlotsAvecCarteSupportee == 0) {
				codeRetour = CKR_AUCUNE_CARTE_SUPPORTEE;
				(*mapSlotsCartes).taille = tailleListeTousLesSlotsAvecCarteSupportee;
			}

			else {

				// Réinitialise l'instantané
				if((*mapSlotsCartes).pSlotsCartes != NULL) {
					memset(&((*mapSlotsCartes).pSlotsCartes),0,sizeof(SLOT_CARTE_PTR));
					free((*mapSlotsCartes).pSlotsCartes);
				}
				(*mapSlotsCartes).taille = tailleListeTousLesSlotsAvecCarteSupportee;


				/* On constitue l'instantané en obtenant les informations de chaque carte supportée */
				(*mapSlotsCartes).pSlotsCartes = (SLOT_CARTE_PTR*) malloc(tailleListeTousLesSlotsAvecCarteSupportee*sizeof(SLOT_CARTE_PTR));
				for(i = 0; i < (int) tailleListeTousLesSlotsAvecCarteSupportee; i++) {

					CK_TOKEN_INFO infosCarte;
					codeRetour = pFonctionsP11->C_GetTokenInfo(pListeTousLesSlotsAvecCarteSupportee[i], &infosCarte);
					
					if(infosCarte.flags & CKF_LOGIN_REQUIRED) {	

						if(codeRetour == CKR_OK) {
							SLOT_CARTE_PTR pSlotCarte = NULL;
							pSlotCarte = (SLOT_CARTE_PTR) calloc(1,sizeof(SLOT_CARTE));
							if(pSlotCarte == NULL)
								codeRetour = CKR_HOST_MEMORY;
							else {
								pSlotCarte->identifiantSlot = pListeTousLesSlotsAvecCarteSupportee[i];
								memcpy(pSlotCarte->numeroSerieCarte,infosCarte.serialNumber,16);
								(*mapSlotsCartes).pSlotsCartes[i] = pSlotCarte;
							}
						} else {

							codeRetour = CKR_ECHEC_CONSTRUCTION_SLOTS_CARTE;
							break;

						}

					} else {
						codeRetour = CKR_TRAITEMENT_NON_SUPPORTE_EN_MODE_SANS_CONTACT;
						break;
					}
				}
			}
		}

		// Liberation de la mémoire attribuée à la liste des slots
		free(pListeTousLesSlotsAvecCarteSupportee);
	}
	else
		codeRetour = CKR_LISTE_FONCTIONS_NON_INITIALISEE;

	return codeRetour;
}

/* 
 * Appelle C_Login et traite le code retour
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @param codeRetour - Le code retour
 * @param typeUtilisateur - Le type d'utilisateur (CK_USER/CK_SO)
 * @param code - Le code à utiliser pour le login
 * @return Code action
 */
CK_ACTION login(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS, CK_RV* codeRetour, unsigned long typeUtilisateur, CK_UTF8CHAR_PTR code) {

	/* Appel à C_Login*/
	*codeRetour = pContexteTS->pFonctionsP11->C_Login(pContexteTS->identifiantSession, typeUtilisateur, code, (CK_ULONG) strlen(code));

	if(*codeRetour != CKR_OK && *codeRetour != CKR_USER_ALREADY_LOGGED_IN) {

		switch(*codeRetour) {

			/* Si l’erreur retournée est l’une des suivantes on réouvre la session et on retente le login */
			case CKR_DEVICE_ERROR:
			case CKR_DEVICE_REMOVED:
			case CKR_SESSION_CLOSED:
			case CKR_SESSION_HANDLE_INVALID:

				return CK_GOTO_REINIT_CONTEXTE;
			
			case CKR_PIN_EXPIRED:

				// C'est le choix de l'implémentation du programme d'exemple de retourner le code d'erreur tel quel
				// Un traitement pour inviter l'utilisateur à faire une modification de code porteur est conseillé
				return CK_GOTO_FIN;

			case CKR_PIN_INCORRECT:

				pContexteTS->flagMessageConformiteCodePorteur = CKF_CODE_PORTEUR_INCORRECT;
				return CK_GOTO_SAISIE_CODE;

			case CKR_PIN_LEN_RANGE:

				pContexteTS->flagMessageConformiteCodePorteur = CKF_LONGUEUR_CODE_PORTEUR_NON_CONFORME;
				return CK_GOTO_SAISIE_CODE;

			case CKR_PIN_INVALID:

				pContexteTS->flagMessageConformiteCodePorteur = CKF_CODE_PORTEUR_NON_CONFORME;
				return CK_GOTO_SAISIE_CODE;

			case CKR_PIN_LOCKED:

				pContexteTS->flagMessageConformiteCodePorteur = CKF_CODE_PORTEUR_INCORRECT;
				pContexteTS->flagMessageEssaisRestantsAvantBlocageCodePorteur = CKF_CODE_PORTEUR_BLOQUE;
				// C'est le choix de l'implémentation du programme d'exemple de redemander la saisie
				// Ainsi la boite de dialogue proposera le recyclage de la carte
				return CK_GOTO_SAISIE_CODE;


			default:

				return CK_GOTO_FIN;

		}

	} else
		*codeRetour = CKR_OK;

	return CK_CONTINUE;
}

/* 
 * Détermine le type d'action retrait Lecteur/Carte qui est survenu
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @param pNumeroSerieCarteManquante - numéro de série de la carte concerné par l'action
 * @param tailleNumSerie - taille du numéro de série de la carte concerné par l'action
 * @return Type d'action
 */
int identifieTypeActionRetrait(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS, unsigned char* pNumeroSerieCarteManquante, int tailleNumSerie) {

	CK_RV codeRetour = CKR_OK;
	CK_SLOT_INFO infosSlot;
	int i = 0;

	/* Recherche de la carte manquante */
	for(i = 0; i < (int) pContexteTS->mapSlotsCartes.taille; i++) {

		/* Récupération des informations du lecteur */
		codeRetour = pContexteTS->pFonctionsP11->C_GetSlotInfo(pContexteTS->mapSlotsCartes.pSlotsCartes[i]->identifiantSlot,&infosSlot);

		if(codeRetour == CKR_OK) {

			/* On a trouvé une carte manquante, c'est celle-ci qu'on considére */
			if((infosSlot.flags & CKF_TOKEN_PRESENT) == 0) {

				strncpy(pNumeroSerieCarteManquante,pContexteTS->mapSlotsCartes.pSlotsCartes[i]->numeroSerieCarte,tailleNumSerie);
				return TYPE_ACTION_RETRAIT_CARTE;

			}

		}

		/* On a trouvé un lecteur manquant, c'est la carte qu'il contenait qu'on considère */
		else if(codeRetour == CKR_SLOT_ID_INVALID) {

			strncpy(pNumeroSerieCarteManquante,pContexteTS->mapSlotsCartes.pSlotsCartes[i]->numeroSerieCarte,tailleNumSerie);
			return TYPE_ACTION_RETRAIT_LECTEUR;

		}

	}

	return TYPE_ACTION_INCONNUE;

}

/* 
 * Scanne tous les slots avec carte pour trouver la carte avec le numéro de série spécifier en argument
 *
 * @param pFonctionsP11 - pointeur sur la liste des fonctions de la librairie PKCS#11
 * @param numeroSerieCarteRecherchee - Numéro de série de la carte recherchée
 * @param pLecteurContenantCarteRecherchee - Lecteur contenant la carte recherchée
 * @return Code retour
 */
CK_RV chercheLecteurContenantCarte(CK_FUNCTION_LIST_PTR pFonctionsP11, unsigned char numeroSerieCarteRecherchee[16], CK_SLOT_ID * pLecteurContenantCarteRecherchee) {

	CK_RV codeRetour = CKR_OK;
	CK_SLOT_ID_PTR pListeTousLesSlotsAvecCarteSupportee = NULL_PTR;
	CK_ULONG tailleListeTousLesSlotsAvecCarteSupportee = 16;
	int nbTentativesSiBufferTropPetit = 4;
	int i = 0;

	/* Si les fonctions de la librairie sont disponibles */
	if(pFonctionsP11 != NULL) {

		pListeTousLesSlotsAvecCarteSupportee = (CK_SLOT_ID_PTR) malloc(tailleListeTousLesSlotsAvecCarteSupportee*sizeof(CK_SLOT_ID));

		/* Récupération de la liste des slots avec carte */
		codeRetour = pFonctionsP11->C_GetSlotList(CK_TRUE, pListeTousLesSlotsAvecCarteSupportee, &tailleListeTousLesSlotsAvecCarteSupportee);

		/* On boucle tant que le buffer pour la liste des slots est trop petit
		   On s'accorde nbTentativesSiBufferTropPetit tentatives */
		while(codeRetour == CKR_BUFFER_TOO_SMALL && i < nbTentativesSiBufferTropPetit) {

			pListeTousLesSlotsAvecCarteSupportee = (CK_SLOT_ID_PTR) realloc(pListeTousLesSlotsAvecCarteSupportee, tailleListeTousLesSlotsAvecCarteSupportee*sizeof(CK_SLOT_ID));

			/* Récupération de la liste des slots avec carte */
			codeRetour = pFonctionsP11->C_GetSlotList(CK_TRUE, pListeTousLesSlotsAvecCarteSupportee, &tailleListeTousLesSlotsAvecCarteSupportee);

			i++;
		}

		if(codeRetour == CKR_OK) {

			if(tailleListeTousLesSlotsAvecCarteSupportee == 0)
				codeRetour = CKR_AUCUNE_CARTE_SUPPORTEE;

			else {

				/* On recherche la carte en récupérant les informations de chaque carte supportée et en comparant le numéro de série */
				for(i = 0; i < (int) tailleListeTousLesSlotsAvecCarteSupportee; i++) {

					CK_TOKEN_INFO infosCarte;
					codeRetour = pFonctionsP11->C_GetTokenInfo(pListeTousLesSlotsAvecCarteSupportee[i], &infosCarte);
					
					if(codeRetour == CKR_OK) {

						/* S'il s'agit de la carte recherchée on récupére l'identifiant du lecteur */
						if(strncmp(numeroSerieCarteRecherchee,infosCarte.serialNumber,16) == 0) {
							*pLecteurContenantCarteRecherchee = pListeTousLesSlotsAvecCarteSupportee[i];
							codeRetour = CKR_CARTE_TROUVEE;
							break;
						} else
							codeRetour = CKR_CARTE_RECHERCHEE_INTROUVABLE;

					} else {

						codeRetour = CKR_ECHEC_CONSTRUCTION_SLOTS_CARTE;
						break;

					}
				}
			}
		}

		// Liberation de la mémoire attribuée à la liste des slots
		free(pListeTousLesSlotsAvecCarteSupportee);

	}
	else
		codeRetour = CKR_LISTE_FONCTIONS_NON_INITIALISEE;

	return codeRetour;
}

/*
 * Récupère l'identifiant d'un objet de données applicatives (jeton)
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @return Code retour
 */
CK_RV recuperationIdentifiantObjetDonneesApplicatives(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS) {

	CK_RV codeRetour = CKR_OK;
	CK_BBOOL vrai = TRUE;
	CK_OBJECT_CLASS classeObjet = CKO_DATA;
	CK_CHAR libelleObjet[] = "CPS_DATA";
	CK_ATTRIBUTE attributs[] = {
		{CKA_CLASS, &classeObjet, sizeof(classeObjet)},
		{CKA_TOKEN, &vrai, sizeof(vrai)},
		{CKA_LABEL, libelleObjet, (CK_ULONG)strlen((char *)libelleObjet)}};
	CK_ULONG tailleAttributs = sizeof(attributs)/sizeof(CK_ATTRIBUTE);

	/* Initialisation de la recherche de l'objet */
	codeRetour = pContexteTS->pFonctionsP11->C_FindObjectsInit(pContexteTS->identifiantSession, attributs, tailleAttributs); 
	if (codeRetour != CKR_OK)
		goto FIN;

	/* Recherche de l'objet */
	while (TRUE) {

		CK_OBJECT_HANDLE objectHandle[] = {0,0};
		CK_ULONG i,nbObjets = 0;

		codeRetour = pContexteTS->pFonctionsP11->C_FindObjects(pContexteTS->identifiantSession, objectHandle, sizeof(objectHandle)/sizeof(CK_OBJECT_HANDLE), &nbObjets);
		if (codeRetour != CKR_OK)
			goto FIN;

		if (nbObjets == 0) {
			codeRetour = CKR_OBJET_RECHERCHE_INTROUVABLE;
			goto FIN;
		}

		for (i=0 ; i<nbObjets ; i++)
		  pContexteTS->identifiantObjet = objectHandle[i];

		if (nbObjets != sizeof(objectHandle)/sizeof(CK_OBJECT_HANDLE))
		  break;

	}

	/* Finalisation de la recherche de l'objet */
	codeRetour = pContexteTS->pFonctionsP11->C_FindObjectsFinal(pContexteTS->identifiantSession);
	if (codeRetour != CKR_OK)
		goto FIN;

	//###
	 FIN:
	//###

		return codeRetour;

}


/*
 * Lit l'attribut valeur d'un objet de données applicatives (jeton)
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @param valeurAttributs - tableau de structure a passé en paramètre du GetAttributeValue pour récupérer le CKA_VALUE
 * @param pTailleValeurAttributs - pointeur sur la taille de valeurAttributs
 * @return Code retour
 */
CK_RV litAttributValeurObjet(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS, CK_ATTRIBUTE valeurAttributs[], CK_ULONG* pTailleValeurAttributs) {

	CK_RV codeRetour = CKR_OK;

	/* Obtention de la taille et d'un pointeur pour stocker la valeur */
	codeRetour = pContexteTS->pFonctionsP11->C_GetAttributeValue(pContexteTS->identifiantSession, pContexteTS->identifiantObjet, valeurAttributs, *pTailleValeurAttributs);
	if (codeRetour != CKR_OK)
		goto FIN;

	/* Allocation et récupération de la valeur */
	valeurAttributs[0].pValue = malloc(valeurAttributs[0].ulValueLen*sizeof(CK_BYTE));
	codeRetour = pContexteTS->pFonctionsP11->C_GetAttributeValue(pContexteTS->identifiantSession, pContexteTS->identifiantObjet, valeurAttributs, *pTailleValeurAttributs);
	if (codeRetour != CKR_OK)
		goto FIN;

	//###/
	 FIN:
	//###/

		return codeRetour;

}

/****************************************/
/* FONCTIONS DE TRAITEMENTS SPECIFIQUES */
/****************************************/

/* 
 * Réalise le traitement spécifique d'état connexion carte
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @return Code retour
 */
CK_RV TS_etatConnexionCarte(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS) {

	/* Réinitialisation */
	reinitialiseIdentifiantsEtInfosContexteTraitementsSpecifiques(pContexteTS);

	return authentification(pContexteTS);

}
/* 
 * Réalise le traitement spécifique d'assistant authentification
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @return Code retour
 */
CK_RV TS_assistantAuthentification(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS) {

	CK_RV codeRetour = CKR_ERREUR_INATTENDUE;
	char * codePorteur = NULL;
	int choixUtilisateur;	

	//################/
	 REINIT_CONTEXTE:
	//################/

	reinitialiseIdentifiantsEtInfosContexteTraitementsSpecifiques(pContexteTS);

	/* Initialisation des flags */
	pContexteTS->flagNbEssaisPossiblesAvantBlocageCodePorteur = CKF_TROIS_ESSAIS_AVANT_BLOCAGE;
	pContexteTS->flagMessageConformiteCodePorteur = CKF_CODE_PORTEUR_CONFORME;

	/* Tant que le code retour ne correspond pas au fait que l’utilisateur est authentifié 
	   on invoque la méthode d'authentification */
	while(codeRetour != CKR_OK) {

		if(authentificationNecessaire(pContexteTS, &codeRetour) == CK_TRUE) {

			// Si lecteur connecté + carte supportée + utilisateur non authentifié, on peut procéder à la saisie du code porteur
			if(codeRetour == CKR_UTILISATEUR_NON_AUTHENTIFIE) {


	//############/
	 SAISIE_CODE:
	//############/

				/* On récupère l'information sur le nombre d'essais restants,  si nécessaire */
				if(pContexteTS->mentionnerNbEssaisRestantsAvantAuthentification) {
					codeRetour = infoNbEssaisCode(CKU_USER, pContexteTS);
					if(codeRetour != CKR_OK) 
						goto FIN;
				}

				/* Demande de saisie du code porteur */
				choixUtilisateur = (*pContexteTS->pDemandeSaisieCodePorteur)(pContexteTS->flagMessageEssaisRestantsAvantBlocageCodePorteur, pContexteTS->flagMessageConformiteCodePorteur, &codePorteur, pContexteTS->infosCarte.serialNumber);

				/* Interprétation du choix utilisateur (Il est possible que le recyclage lui ait été proposé)*/
				if(choixUtilisateur == ANNULATION) {

					codeRetour = CKR_INTERRUPTION_UTILISATEUR;
					goto FIN;

				} else if(choixUtilisateur == DEMANDE_RECYCLAGE) {

					codeRetour = CKR_DEMANDE_RECYCLAGE;
					goto FIN;

				}

				/* Vérification de la conformité du code porteur, si nécessaire */
				if(pContexteTS->verifierConformiteCodeAvantAuthentification) {
					codeRetour = infoConformiteCode(pContexteTS, codePorteur);

					/* Si le code n'est pas conforme on redemande la saisie */
					if(codeRetour == CKR_PIN_INVALID || codeRetour == CKR_PIN_LEN_RANGE)
						goto SAISIE_CODE;

					else if(codeRetour != CKR_OK)
						goto FIN;

				}

				/* On effectue le Login */
				switch(login(pContexteTS, &codeRetour, CKU_USER, (CK_UTF8CHAR_PTR) codePorteur)) {

					case CK_GOTO_FIN:
					default:
						
						if(codeRetour == CKR_OK)
							codeRetour = CKR_UTILISATEUR_AUTHENTIFIE;
						goto FIN;
					
					case CK_GOTO_SAISIE_CODE:

						goto SAISIE_CODE;

					case CK_GOTO_REINIT_CONTEXTE:

						goto REINIT_CONTEXTE;

				}

			}

		} else {

			// Si l'utilisateur interrompt ou si erreur PKCS#11 on sort de la boucle
			if(codeRetour != CKR_OK)
				goto FIN;

		}
	}


	//###/
	 FIN:
	//###/

		// RAZ et libération du code
		if(codePorteur != NULL) {
			memset(codePorteur,0,strlen(codePorteur));
			free(codePorteur);
		}

		return codeRetour;

}

/* 
 * Réalise le traitement spécifique d'état de saisie du code porteur
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @return Code retour
 */
CK_RV TS_etatSaisieCodePorteur(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS) {

	CK_RV codeRetour = CKR_OK;
	char * codePorteur = NULL;
	int choixUtilisateur;	

	//################/
	 REINIT_CONTEXTE:
	//################/

	reinitialiseIdentifiantsEtInfosContexteTraitementsSpecifiques(pContexteTS);

	/* Initialisation des flags */
	pContexteTS->flagNbEssaisPossiblesAvantBlocageCodePorteur = CKF_N_ESSAIS_AVANT_BLOCAGE;
	pContexteTS->flagMessageConformiteCodePorteur = CKF_CODE_PORTEUR_CONFORME;
	
	codeRetour = recuperePremiereCarte(pContexteTS);

	/* Si on a une carte supportée */
	if(codeRetour == CKR_OK) {

		/* On ferme toutes les sessions ouvertes dessus */
		codeRetour = pContexteTS->pFonctionsP11->C_CloseAllSessions(pContexteTS->identifiantSlot);

		if(codeRetour == CKR_OK) {

			/*********************/
			/* OUVERTURE SESSION */
			/*********************/
			codeRetour = ouvreSession(pContexteTS);

			if(codeRetour == CKR_OK) {

				/* Si l'on a pas préalablement récupéré les infos cartes on le fait pour savoir si l'authentification est nécessaire */
				if(strlen(pContexteTS->infosCarte.serialNumber) == 0) {
					
					/*	Obtention des informations de la carte */
					codeRetour = pContexteTS->pFonctionsP11->C_GetTokenInfo(pContexteTS->identifiantSlot, &(pContexteTS->infosCarte));
					if(codeRetour != CKR_OK)
						goto REINIT_CONTEXTE;

				}

				if(pContexteTS->infosCarte.flags & CKF_LOGIN_REQUIRED) {

					/* On invoque la fonction du choix du nombre d'essais avant blocage du code porteur */
					switch((*pContexteTS->pDemandeChoixNbEssaisPossiblesAvantBlocageCodePorteur)()) {

						case ANNULATION:

							codeRetour = CKR_INTERRUPTION_UTILISATEUR;
							break;

						case N_TENTATIVES:

							/* Mise à jour du flag en fonction du choix utilisateur */
							pContexteTS->flagNbEssaisPossiblesAvantBlocageCodePorteur = CKF_N_ESSAIS_AVANT_BLOCAGE;


	//#########################/
	 SAISIE_CODE_N_TENTATIVES:
	//#########################/

						/* On récupère l'information sur le nombre d'essais restants,  si nécessaire */
							if(pContexteTS->mentionnerNbEssaisRestantsAvantAuthentification) {
								codeRetour = infoNbEssaisCode(CKU_USER, pContexteTS);
								if(codeRetour != CKR_OK) 
									goto FIN;
							}
						
							/* Demande de saisie du code porteur */
							choixUtilisateur = (*pContexteTS->pDemandeSaisieCodePorteur)(pContexteTS->flagMessageEssaisRestantsAvantBlocageCodePorteur, pContexteTS->flagMessageConformiteCodePorteur, &codePorteur, pContexteTS->infosCarte.serialNumber);

							/* Interprétation du choix utilisateur (Il est possible que le recyclage lui ait été proposé)*/
							if(choixUtilisateur == ANNULATION) {

								codeRetour = CKR_INTERRUPTION_UTILISATEUR;
								goto FIN;

							} else if(choixUtilisateur == DEMANDE_RECYCLAGE) {

								codeRetour = CKR_DEMANDE_RECYCLAGE;
								goto FIN;

							}

							/* Vérification de la conformité du code porteur, si nécessaire */
							if(pContexteTS->verifierConformiteCodeAvantAuthentification) {
								codeRetour = infoConformiteCode(pContexteTS, codePorteur);

								/* Si le code n'est pas conforme on redemande la saisie */
								if(codeRetour == CKR_PIN_INVALID || codeRetour == CKR_PIN_LEN_RANGE)
									goto SAISIE_CODE_N_TENTATIVES;

								else if(codeRetour != CKR_OK)
									goto FIN;

							}

							/* On effectue le Login */
							switch(login(pContexteTS, &codeRetour, CKU_USER, (CK_UTF8CHAR_PTR) codePorteur)) {

								case CK_GOTO_FIN:
								default:

									if(codeRetour == CKR_OK)
										codeRetour = CKR_UTILISATEUR_AUTHENTIFIE;
									goto FIN;
								
								case CK_GOTO_SAISIE_CODE:

									goto SAISIE_CODE_N_TENTATIVES;

								case CK_GOTO_REINIT_CONTEXTE:

									goto REINIT_CONTEXTE;

							}

							break;


						case TROIS_TENTATIVES:

							/* Mise à jour du flag en fonction du choix utilisateur */
							pContexteTS->flagNbEssaisPossiblesAvantBlocageCodePorteur = CKF_TROIS_ESSAIS_AVANT_BLOCAGE;


		//########################/
		 SAISIE_CODE_3_TENTATIVES:
		//########################/

							/* On récupère l'information sur le nombre d'essais restants,  si nécessaire */
							if(pContexteTS->mentionnerNbEssaisRestantsAvantAuthentification) {
								codeRetour = infoNbEssaisCode(CKU_USER, pContexteTS);
								if(codeRetour != CKR_OK) 
									goto FIN;
							}
						
							/* On invoque la fonction du demande de saisie du code porteur */
							choixUtilisateur = (*pContexteTS->pDemandeSaisieCodePorteur)(pContexteTS->flagMessageEssaisRestantsAvantBlocageCodePorteur, pContexteTS->flagMessageConformiteCodePorteur, &codePorteur, pContexteTS->infosCarte.serialNumber);

							/* Interprétation du choix utilisateur (Il est possible que le recyclage lui ait été proposé)*/
							if(choixUtilisateur == ANNULATION) {

								codeRetour = CKR_INTERRUPTION_UTILISATEUR;
								goto FIN;

							} else if(choixUtilisateur == DEMANDE_RECYCLAGE) {

								codeRetour = CKR_DEMANDE_RECYCLAGE;
								goto FIN;

							}

							/* Vérification de la conformité du code porteur, si nécessaire */
							if(pContexteTS->verifierConformiteCodeAvantAuthentification) {
								codeRetour = infoConformiteCode(pContexteTS, codePorteur);

								/* Si le code n'est pas conforme on redemande la saisie */
								if(codeRetour == CKR_PIN_INVALID || codeRetour == CKR_PIN_LEN_RANGE)
									goto SAISIE_CODE_3_TENTATIVES;

								else if(codeRetour != CKR_OK)
									goto FIN;


							}

							/* On effectue le Login */
							switch(login(pContexteTS, &codeRetour, CKU_USER, (CK_UTF8CHAR_PTR) codePorteur)) {

								case CK_GOTO_FIN:
								default:

									if(codeRetour == CKR_OK)
										codeRetour = CKR_UTILISATEUR_AUTHENTIFIE;
									goto FIN;
								
								case CK_GOTO_SAISIE_CODE:

									goto SAISIE_CODE_3_TENTATIVES;

								case CK_GOTO_REINIT_CONTEXTE:

									goto REINIT_CONTEXTE;

							}

							break;

						default:

							codeRetour = CKR_ERREUR_INATTENDUE;
							break;

					} 

				} else {
					codeRetour = CKR_TRAITEMENT_NON_SUPPORTE_EN_MODE_SANS_CONTACT;
					goto FIN;					
				}
			}
		}
	}

	//###/
	 FIN:
	//###/


		// RAZ et libération du code
		if(codePorteur != NULL) {
			memset(codePorteur,0,strlen(codePorteur));
			free(codePorteur);
		}

		return codeRetour;

}

/* 
 * Réalise le traitement spécifique de recyclage du code porteur
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @return Code retour
 */
CK_RV TS_recyclageCodePorteur(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS) {

	CK_RV codeRetour = CKR_OK;
	char * codeDeblocage = NULL;
	char * nouveauCodePorteur1 = NULL;
	char * nouveauCodePorteur2 = NULL;
	int choixUtilisateur;
	
	reinitialiseIdentifiantsEtInfosContexteTraitementsSpecifiques(pContexteTS);

	/* Initialisation des flags */
	pContexteTS->flagMessageConformiteCodePorteur = CKF_CODE_PORTEUR_CONFORME;
	pContexteTS->flagMessageConformiteCodeDeblocage = CKF_CODE_DEBLOCAGE_CONFORME;
	
	/* Récupération de la première carte */
	codeRetour = recuperePremiereCarte(pContexteTS);

	/* Si on a une carte supportée */
	if(codeRetour == CKR_OK) {


	//#########/
	 RECYCLAGE:
	//#########/


		/* On ferme toutes les sessions ouvertes sur la carte */
		codeRetour = pContexteTS->pFonctionsP11->C_CloseAllSessions(pContexteTS->identifiantSlot);

		if(codeRetour == CKR_OK) {

			/*********************/
			/* OUVERTURE SESSION */
			/*********************/
			codeRetour = ouvreSession(pContexteTS);

			if(codeRetour == CKR_OK) {

				if(pContexteTS->mentionnerNbEssaisRestantsAvantAuthentification) {

					codeRetour = infoNbEssaisCode(CKU_SO, pContexteTS);
					if(codeRetour != CKR_OK) 
						goto FIN;

				}

				/* Si l'on a pas préalablement récupéré les infos cartes on le fait pour savoir si l'authentification est nécessaire */
				if(strlen(pContexteTS->infosCarte.serialNumber) == 0) {
					
					/*	Obtention des informations de la carte */
					codeRetour = pContexteTS->pFonctionsP11->C_GetTokenInfo(pContexteTS->identifiantSlot, &(pContexteTS->infosCarte));
					if(codeRetour != CKR_OK)
						goto RECYCLAGE;

				}

				if(pContexteTS->infosCarte.flags & CKF_LOGIN_REQUIRED) {

					/* Demande de recyclage */
					choixUtilisateur = (*pContexteTS->pDemandeRecyclageCodePorteur)(pContexteTS->flagMessageEssaisRestantsAvantBlocageCodeDeblocage, pContexteTS->flagMessageConformiteCodeDeblocage, pContexteTS->flagMessageConformiteCodePorteur, &codeDeblocage, &nouveauCodePorteur1, &nouveauCodePorteur2, pContexteTS->infosCarte.serialNumber);

					if(choixUtilisateur == ANNULATION) {

						codeRetour = CKR_INTERRUPTION_UTILISATEUR;
						goto FIN;

					} else {

						codeRetour = CKR_OK;

						/* Contrôle pour s’assurer que les 2 nouveaux codes porteurs saisis coïncident */
						if(strcmp(nouveauCodePorteur1,nouveauCodePorteur2) != 0) {

							pContexteTS->flagMessageConformiteCodePorteur = CKF_CODES_DIFFERENTS;
							goto RECYCLAGE;

						} else {

							/***********************/
							/* AUTHENTIFICATION SO */
							/***********************/
							codeRetour = pContexteTS->pFonctionsP11->C_Login(pContexteTS->identifiantSession, CKU_SO, (CK_UTF8CHAR_PTR) codeDeblocage, (CK_ULONG) strlen(codeDeblocage));

							if(codeRetour != CKR_OK && codeRetour != CKR_USER_ALREADY_LOGGED_IN) {

								switch(codeRetour) {

									/* Si l’erreur retournée est l’une des suivantes on réessaie */
									case CKR_DEVICE_ERROR:
									case CKR_DEVICE_REMOVED:
									case CKR_SESSION_CLOSED:
									case CKR_SESSION_HANDLE_INVALID:

										goto RECYCLAGE;
									
									case CKR_PIN_EXPIRED:

										// C'est le choix de l'implémentation du programme d'exemple de retourner le code d'erreur tel quel
										// Un traitement pour inviter l'utilisateur à faire une modification de code porteur est conseillé
										goto FIN;

									case CKR_PIN_INCORRECT:

										pContexteTS->flagMessageConformiteCodePorteur = CKF_CODE_DEBLOCAGE_INCORRECT;
										goto RECYCLAGE;

									case CKR_PIN_LEN_RANGE:

										pContexteTS->flagMessageConformiteCodePorteur = CKF_LONGUEUR_CODE_DEBLOCAGE_NON_CONFORME;
										goto RECYCLAGE;

									case CKR_PIN_INVALID:

										pContexteTS->flagMessageConformiteCodePorteur = CKF_CODE_DEBLOCAGE_NON_CONFORME;
										goto RECYCLAGE;

									case CKR_PIN_LOCKED:

										pContexteTS->flagMessageConformiteCodePorteur = CKF_CODE_DEBLOCAGE_INCORRECT;
										pContexteTS->flagMessageEssaisRestantsAvantBlocageCodePorteur = CKF_CODE_DEBLOCAGE_BLOQUE;
										// C'est le choix de l'implémentation du programme d'exemple de redemander la saisie
										// Ainsi la boite de dialogue proposera le recyclage de la carte ou l'annulation de l'authentification
										goto RECYCLAGE;


									default:

										goto FIN;

								}

							} else {

								/* Vérification de la conformité du code, si nécessaire */
								if(pContexteTS->verifierConformiteCodeAvantAuthentification) {

									codeRetour = infoConformiteCode(pContexteTS, nouveauCodePorteur1);

									/* Si le code n'est pas conforme on redemande la saisie */
									if(codeRetour == CKR_PIN_INVALID || codeRetour == CKR_PIN_LEN_RANGE)
										goto RECYCLAGE;

									else if(codeRetour != CKR_OK)
										goto FIN;	
								}

								/* Recyclage */
								codeRetour = pContexteTS->pFonctionsP11->C_InitPIN(pContexteTS->identifiantSession,(CK_UTF8CHAR_PTR) nouveauCodePorteur1, (CK_ULONG) strlen(nouveauCodePorteur1));

								/* Carte recyclée */
								if(codeRetour == CKR_OK)
									pContexteTS->pFonctionsP11->C_Logout(pContexteTS->identifiantSession);

								else {
									
									switch(codeRetour) {

										/* Si l’erreur retournée est l’une des suivantes on réessaie */
										case CKR_DEVICE_ERROR:
										case CKR_DEVICE_REMOVED:
										case CKR_SESSION_CLOSED:
										case CKR_SESSION_HANDLE_INVALID:

											goto RECYCLAGE;
										
										default:

											goto FIN;

									}
								}

							}

						}
					}

					goto FIN;

				} else {
					codeRetour = CKR_TRAITEMENT_NON_SUPPORTE_EN_MODE_SANS_CONTACT;
					goto FIN;
				}

			}
		}
	} 

	//###/
	 FIN:
	//###/

		// RAZ et liberation des codes
		if(codeDeblocage != NULL) {
			memset(codeDeblocage,0,strlen(codeDeblocage));
			free(codeDeblocage);
		}
		if(nouveauCodePorteur1 != NULL) {
			memset(nouveauCodePorteur1,0,strlen(nouveauCodePorteur1));
			free(nouveauCodePorteur1);
		}
		if(nouveauCodePorteur2 != NULL) {
			memset(nouveauCodePorteur2,0,strlen(nouveauCodePorteur2));
			free(nouveauCodePorteur2);
		}

		return codeRetour;

}


/* 
 * Réalise le traitement spécifique d'authentification après un retrait carte ou lecteur
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @param typeRetrait - Type de retrait
 * @return Code retour
 */
CK_RV TS_authentificationApresRetrait(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS, int typeRetrait) {

	CK_RV codeRetour = CKR_OK;
	int choixUtilisateur;
	int typeAction = TYPE_ACTION_INCONNUE;
	unsigned char numeroSerieCarteManquante[16];
	CK_SLOT_ID lecteurIdentifie = -1; // Lecteur ayant subit l'action (retrait/insertion/connexion)
	char * codePorteur = NULL;

	//################/
	 REINIT_CONTEXTE:
	//################/


		reinitialiseIdentifiantsEtInfosContexteTraitementsSpecifiques(pContexteTS);

		/*--------------------------------------------------------------------------/
		/              SAUVEGARDE DU CONTEXTE SLOTS/CARTES
		/---------------------------------------------------------------------------/
		/ Sauvegarde d’une table de correspondance entre les identifiants 
		/ de slot et les numéros de série des cartes, afin d'identifier
		/ la carte retirée.
		/--------------------------------------------------------------------------*/
		codeRetour = instantane(pContexteTS->pFonctionsP11,&(pContexteTS->mapSlotsCartes));

		if(codeRetour == CKR_OK) {

			/*--------------------------------------------------------------------------/
			/              SIMULATION RETRAIT CARTE/LECTEUR
			/---------------------------------------------------------------------------/
			/ Traitement propre au programme d'exemple pour détecter un 
			/ retrait de carte.
			/ Dans les conditions réelles, la disparition, soit par retrait lecteur, 
			/ soit par retrait carte, d'une carte authentifiée se traduit par 
			/ un code retour SESSION_HANDLE_INVALIDE lors d'une opération 
			/ (ex: signature, empreinte...)
			/ Se réferer à la fonction identifieTypeActionRetrait pour savoir
			/ comment déterminer s'il s'agit de retrait lecteur ou d'un retrait carte
			/ 
			/ Se référer à la partie REAUTHENTIFICATION CARTE ci-après
			/ pour le code à executer pour réauthentifier la carte retirée
			/---------------------------------------------------------------------------*/

			/* Demande de retrait */
			choixUtilisateur = pContexteTS->pDemandeActionLecteurCarte((typeRetrait == TYPE_RETRAIT_CARTE)?TYPE_ACTION_RETRAIT_CARTE:TYPE_ACTION_RETRAIT_LECTEUR,NULL);

			if(choixUtilisateur == ANNULATION) {

				codeRetour = CKR_INTERRUPTION_UTILISATEUR;
				goto FIN;

			} else {

				/* Identification du type de retrait et récupération du numéro de série de la carte à réauthentifier */
				typeAction = identifieTypeActionRetrait(pContexteTS,numeroSerieCarteManquante,16);
			
				/* S'il s'agit d'un retrait, on demande la réinsertion de la carte à réauthentifier */
				if((typeRetrait == TYPE_RETRAIT_CARTE && typeAction == TYPE_ACTION_RETRAIT_CARTE) 
					|| (typeRetrait == TYPE_RETRAIT_LECTEUR && typeAction == TYPE_ACTION_RETRAIT_LECTEUR))
						goto DEMANDE_PRESENTATION_CARTE;

				/* On ne traite pas le cas du retrait lecteur dans le traitement spécifique du retrait carte et inversement */
				else if((typeRetrait == TYPE_RETRAIT_CARTE && typeAction == TYPE_ACTION_RETRAIT_LECTEUR) 
					|| (typeRetrait == TYPE_RETRAIT_LECTEUR && typeAction == TYPE_ACTION_RETRAIT_CARTE)) {

					codeRetour = CKR_OPERATION_NON_CONFORME;
					goto FIN;

				}

				/* Sinon on redemande le retrait */
				else
					goto REINIT_CONTEXTE;

			}


	//##########################/
	 DEMANDE_PRESENTATION_CARTE:
	//##########################/

			
			/*-------------------------------------------------------/
			/      REAUTHENTIFICATION CARTE : DEBUT
			/-------------------------------------------------------*/

			/* Demande de présentation carte */
			choixUtilisateur = pContexteTS->pDemandeActionLecteurCarte((typeRetrait == TYPE_RETRAIT_CARTE)?TYPE_ACTION_INSERTION_CARTE:TYPE_ACTION_CONNEXION_LECTEUR,numeroSerieCarteManquante);

			if(choixUtilisateur == ANNULATION) {

				codeRetour = CKR_INTERRUPTION_UTILISATEUR;
				goto FIN;

			} else {

				/* Recherche de la carte retirée et récupération du l'identifiant lecteur dans lequel elle apparaît */
				codeRetour = chercheLecteurContenantCarte(pContexteTS->pFonctionsP11,numeroSerieCarteManquante,&lecteurIdentifie);

				if(codeRetour == CKR_CARTE_TROUVEE) {

					/* Alerte l'utilisateur que la carte a été carte retrouvée */ 
					pContexteTS->pDeclencheAlerteCarteRetrouvee();

					/* Mise à jour de l'identifiant lecteur du contexte */
					pContexteTS->identifiantSlot = lecteurIdentifie;



	//###########/
	 SAISIE_CODE:
	//###########/

					/* On récupère l'information sur le nombre d'essais restants,  si nécessaire */
					if(pContexteTS->mentionnerNbEssaisRestantsAvantAuthentification) {

						codeRetour = infoNbEssaisCode(CKU_USER, pContexteTS);
						if(codeRetour != CKR_OK) 
							goto FIN;

					}

					/* Si l'on a pas préalablement récupéré les infos cartes on le fait pour savoir si l'authentification est nécessaire */
					if(strlen(pContexteTS->infosCarte.serialNumber) == 0) {
						
						/*	Obtention des informations de la carte */
						codeRetour = pContexteTS->pFonctionsP11->C_GetTokenInfo(pContexteTS->identifiantSlot, &(pContexteTS->infosCarte));
						if(codeRetour != CKR_OK)
							goto DEMANDE_PRESENTATION_CARTE;

					}

					if(pContexteTS->infosCarte.flags & CKF_LOGIN_REQUIRED) {

						/* Demande de saisie du code porteur */
						choixUtilisateur = (*pContexteTS->pDemandeSaisieCodePorteur)(pContexteTS->flagMessageEssaisRestantsAvantBlocageCodePorteur, pContexteTS->flagMessageConformiteCodePorteur, &codePorteur, pContexteTS->infosCarte.serialNumber);

						/* Interprétation du choix utilisateur (Il est possible que le recyclage lui ait été proposé)*/
						if(choixUtilisateur == ANNULATION) {

							codeRetour = CKR_INTERRUPTION_UTILISATEUR;
							goto FIN;

						} else if(choixUtilisateur == DEMANDE_RECYCLAGE) {

							codeRetour = CKR_DEMANDE_RECYCLAGE;
							goto FIN;

						}

						/* Vérification de la conformité du code porteur, si nécessaire */
						if(pContexteTS->verifierConformiteCodeAvantAuthentification) {

							codeRetour = infoConformiteCode(pContexteTS, codePorteur);

							/* Si le code n'est pas conforme on redemande la saisie */
							if(codeRetour == CKR_PIN_INVALID || codeRetour == CKR_PIN_LEN_RANGE)
								goto SAISIE_CODE;

							else if(codeRetour != CKR_OK)
								goto FIN;				
						}

						/* Ouverture d'une session pour authentifier */
						codeRetour = ouvreSession(pContexteTS);

						if(codeRetour != CKR_OK) {
						
							switch(codeRetour) {

								/* Si l’erreur retournée est l’une des suivantes on réessaie */
								case CKR_DEVICE_ERROR:
								case CKR_DEVICE_REMOVED:
								case CKR_SLOT_ID_INVALID:
								case CKR_TOKEN_NOT_PRESENT:
								case CKR_TOKEN_NOT_RECOGNIZED:

									goto DEMANDE_PRESENTATION_CARTE;

							}
						}

						/* Si la session est ouverte, on effectue le Login */
						switch(login(pContexteTS, &codeRetour, CKU_USER, (CK_UTF8CHAR_PTR) codePorteur)) {

							case CK_GOTO_FIN:
							default:
								codeRetour = CKR_UTILISATEUR_AUTHENTIFIE;
								goto FIN;
							
							case CK_GOTO_SAISIE_CODE:
								goto SAISIE_CODE;

							case CK_GOTO_REINIT_CONTEXTE:

								goto REINIT_CONTEXTE;

						}

					} else {
						codeRetour = CKR_TRAITEMENT_NON_SUPPORTE_EN_MODE_SANS_CONTACT;
						goto FIN;
					}

				} else {

					codeRetour = CKR_CARTE_RECHERCHEE_INTROUVABLE;
					goto FIN;

				}

			}
			/*---------------------------------------------------------------------------/
			/          REAUTHENTIFICATION CARTE ARRACHEE : FIN
			/---------------------------------------------------------------------------*/

		}

	//####/
	 FIN:
	//####/

		// RAZ et libération du code
		if(codePorteur != NULL) {
			memset(codePorteur,0,strlen(codePorteur));
			free(codePorteur);
		}

		return codeRetour;

}

/* 
 * Réalise la lecture d'un objet de données applicative (jeton)
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @param valeurAttributs - tableau de structure a passé en paramètre du GetAttributeValue pour récupérer le CKA_VALUE
 * @param pTailleValeurAttributs - pointeur sur la taille de valeurAttributs
 * @return Code retour 
 */
CK_RV TS_lectureObjetDonneesApplicatives(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS, CK_ATTRIBUTE valeurAttributs[], CK_ULONG* pTailleValeurAttributs) {

	CK_RV codeRetour = CKR_OK;

	//#####/
	 DEBUT:
	//#####/	

	/* Authentification */
	codeRetour = TS_assistantAuthentification(pContexteTS);

	/* Attention dans ce traitement le mode sans contact est supportée, 
	   donc on effectue le traitement  lorsque l'utilisateur est authentifié ou lorsque l'on a une carte en sans contact */ 
	if(codeRetour == CKR_UTILISATEUR_AUTHENTIFIE || codeRetour == CKR_TRAITEMENT_NON_SUPPORTE_EN_MODE_SANS_CONTACT) {

		codeRetour = recuperationIdentifiantObjetDonneesApplicatives(pContexteTS);

		/* Lecture de l'attribut de l'objet */
		if(codeRetour == CKR_OK) {
		
			/* Vérification de validité de l'identifiant d'objet */
			if (pContexteTS->identifiantObjet == 0) {
				codeRetour = CKR_OBJET_RECHERCHE_INTROUVABLE; // c'est probablement une carte CPS2ter, pas d'objet trouvé donc
				goto FIN;
			}

			codeRetour = litAttributValeurObjet(pContexteTS, valeurAttributs, pTailleValeurAttributs);

			if(codeRetour != CKR_OK)
				goto TRAITEMENT_ERREUR;

		} else
			goto TRAITEMENT_ERREUR;

	} else
		goto FIN;

	//#################/
	 TRAITEMENT_ERREUR:
	//#################/

	switch(codeRetour) {

		/* Si l’erreur retournée est l’une des suivantes on réessaie */
		case CKR_DEVICE_ERROR:
		case CKR_DEVICE_REMOVED:
		case CKR_SESSION_CLOSED:
		case CKR_SESSION_HANDLE_INVALID:
			goto DEBUT;

		default:
			goto FIN;

	}

	//#####/
	 FIN:
	//#####/
	
	pContexteTS->pFonctionsP11->C_CloseSession(pContexteTS->identifiantSession);

	// valeurAttributs[0].pValue est libéré au niveau au dessus (TraitementsSpecifiques->Java_fr_asip_cps3_exemple_modele_jni_TraitementsSpecifiquesImplementation_lectureObjetDonneesApplicatives)
	return codeRetour;

}

/* 
 * Réalise la modification d'un objet de données applicatives (jeton)
 *
 * @param pContexteTS - pointeur sur le contexte de réalisation des appels spécifiques
 * @param valeurAttributs - tableau de structure a passé en paramètre du GetAttributeValue pour récupérer le CKA_VALUE
 * @param pTailleValeurAttributs - pointeur sur la taille de valeurAttributs
 * @return Code retour 
 */
CK_RV TS_modificationObjetDonneesApplicatives(CONTEXTE_TRAITEMENTS_SPECIFIQUES_PTR pContexteTS, CK_ATTRIBUTE valeurAttributs[], CK_ULONG* pTailleValeurAttributs) {

	CK_RV codeRetour = CKR_OK;
	CK_BBOOL estModifiable = FALSE;
	CK_ATTRIBUTE valeurAttributsModifiee[] = {{CKA_VALUE, NULL, 0}};
	CK_ULONG tailleValeurAttributsModifiee = sizeof(valeurAttributsModifiee)/sizeof(CK_ATTRIBUTE);
	CK_ATTRIBUTE attributModifiable[] = {{CKA_MODIFIABLE, &estModifiable, sizeof(estModifiable)}};
	CK_ULONG tailleAttributModifiable = sizeof(attributModifiable)/sizeof(CK_ATTRIBUTE);
	int choixUtilisateur;

	//#####/
	 DEBUT:
	//#####/	

	/* Authentification */
	codeRetour = TS_assistantAuthentification(pContexteTS);
	
	if(codeRetour == CKR_MODE_SANS_CONTACT_UTILISATEUR_NON_AUTHENTIFIE)
		codeRetour = CKR_TRAITEMENT_NON_SUPPORTE_EN_MODE_SANS_CONTACT;

	if(codeRetour == CKR_UTILISATEUR_AUTHENTIFIE) {

		codeRetour = recuperationIdentifiantObjetDonneesApplicatives(pContexteTS);

		if (codeRetour != CKR_OK)
			goto TRAITEMENT_ERREUR;

		/* Vérification de validité de l'identifiant d'objet */
		if (pContexteTS->identifiantObjet == 0) {
			codeRetour = CKR_AUCUN_OBJET_A_MODIFIER; // c'est probablement une carte CPS2ter, rien à modifier donc
			goto FIN;
		}

		/* S'assurer que l'objet est modifiable */
		codeRetour = pContexteTS->pFonctionsP11->C_GetAttributeValue(pContexteTS->identifiantSession, pContexteTS->identifiantObjet, attributModifiable, tailleAttributModifiable);
		if (codeRetour != CKR_OK)
			goto TRAITEMENT_ERREUR;
		else {
			if(estModifiable == FALSE) {
				codeRetour = CKR_OBJET_EXISTE_MAIS_NON_MODIFIABLE;
				goto FIN;
			}
		}

		/* LECTURE objet à modifier */
		codeRetour = litAttributValeurObjet(pContexteTS, valeurAttributs, pTailleValeurAttributs);
		if (codeRetour != CKR_OK)
			goto TRAITEMENT_ERREUR;

		/* Demande de modification de l'attribut de l'objet */
		choixUtilisateur = pContexteTS->pDemandeModificationObjet(valeurAttributs[0].pValue,
		                                                          valeurAttributs[0].ulValueLen,
																  (CK_BYTE_PTR *)&(valeurAttributsModifiee[0].pValue),
																  &(valeurAttributsModifiee[0].ulValueLen)
																);
		if(choixUtilisateur == ANNULATION) {
			codeRetour = CKR_INTERRUPTION_UTILISATEUR;
			goto FIN;
		}

		/* MODIFICATION de l'attribut */
		codeRetour = pContexteTS->pFonctionsP11->C_SetAttributeValue(pContexteTS->identifiantSession, pContexteTS->identifiantObjet, valeurAttributsModifiee, tailleValeurAttributsModifiee);
		if (codeRetour != CKR_OK)
			goto TRAITEMENT_ERREUR;

		/* VERIFICATION DE LA MODIFICATION */
		codeRetour = litAttributValeurObjet(pContexteTS, valeurAttributs, pTailleValeurAttributs);
		if (codeRetour != CKR_OK)
			goto TRAITEMENT_ERREUR;
		if (memcmp(valeurAttributs[0].pValue, valeurAttributsModifiee[0].pValue, valeurAttributs[0].ulValueLen)!=0) {
			codeRetour = CKR_DATA_INVALID;
			goto FIN;
		}

	} else
		goto FIN;

	//#################/
	 TRAITEMENT_ERREUR:
	//#################/

	switch(codeRetour) {

		/* Si l’erreur retournée est l’une des suivantes on réessaie */
		case CKR_DEVICE_ERROR:
		case CKR_DEVICE_REMOVED:
		case CKR_SESSION_CLOSED:
		case CKR_SESSION_HANDLE_INVALID:
			goto DEBUT;

		default:
			goto FIN;

	}

	//####/
	 FIN:
	//####/

	pContexteTS->pFonctionsP11->C_CloseSession(pContexteTS->identifiantSession);

	if (valeurAttributsModifiee[0].pValue != NULL)
		free(valeurAttributsModifiee[0].pValue);
	//valeurAttributs[0].pValue est libéré au niveau au dessus (TraitementsSpecifiques->Java_fr_asip_cps3_exemple_modele_jni_TraitementsSpecifiquesImplementation_modificationObjetDonneesApplicatives)

	return codeRetour;
}
